﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NPipeline.Event;


namespace NPipeline.Driver
{
    public class SynchronousStageDriver : BaseStageDriver, IStageDriver
    {
        private Queue<object> queue = new Queue<object>();


        // create a nested class
        private class SimpleFeeder : IFeeder
        {
            public SynchronousStageDriver driver;

            public SimpleFeeder (SynchronousStageDriver driver)
	        {
                this.driver = driver;
        	}

            public void Feed(object obj)
            {
                if (driver.currentState == PipelineState.ERROR)
                    throw new ArgumentException("Unable to process data; driver in fatal error state");

                // enqueue the object for later processing
                if (driver.currentState != PipelineState.RUNNING)
                {
                    driver.queue.Enqueue(obj);
                    return;
                }

                try
                {
                    driver.Stage.Process(obj);
                }
                catch (StageException e)
                {
                    // TODO: review this if this logic is ok at all
                    // notify Pipeline about Error condition
                    driver.context.RaiseStageErrorEvent(
                        new StageErrorEventArgs(driver.Stage, StageState.PROCESS, obj, e));


                    driver.recordProcessingException(obj, e);
                    // add handling for FaultTolerance ...
                }
            }
        }

        private IFeeder feeder;

        public SynchronousStageDriver(IStage stage, IStageContext context) :
            base(stage,context)
        {

            feeder = new SimpleFeeder(this);

        }

        // check what the new keyword does ??
        public void Start()
        {
            if (currentState == PipelineState.STOPPED)
            {
                try
                {
                    Stage.PreProcess();
                    SetState(PipelineState.RUNNING);
                }
                catch (StageException e)
                {
                    // TODO: review this if this logic is ok at all
                    // notify Pipeline about Error condition
                    this.context.RaiseStageErrorEvent(
                        new StageErrorEventArgs(Stage, StageState.PREPROCESS, null, e));
                    fatalError(e);
                }

                // feed any queued values before returning control
                while (queue.Count > 0)
                {
                    Feeder.Feed(queue.Dequeue());
                }
            } else {
                throw new ArgumentException("Illegal atempt to start driver from state: {0}",
                    currentState.ToString());


            }
        }

        // check what the new keyword does ?? or just don't implement it in the BaseClass 
        public void Finish()
        {
            if (currentState == PipelineState.RUNNING)
            {
                try {
                    TestAndSetState(PipelineState.RUNNING, PipelineState.STOP_REQUESTED);
                    if (currentState == PipelineState.STOP_REQUESTED)
                    {
                        Stage.PostProcess();
                    }
                }
                catch (StageException e) {
                    // TODO: review this if this logic is ok at all
                    // notify Pipeline about Error condition
                    this.context.RaiseStageErrorEvent(
                        new StageErrorEventArgs(Stage, StageState.POSTPROCESS,null, e));

                    // record error
                    fatalError(e);

                }
                finally {
                    Stage.Release();
                    TestAndSetState(PipelineState.STOP_REQUESTED, PipelineState.STOPPED);
                }
            } else {
                throw new ArgumentException("Driver is not running: current state: {0}",
                    currentState.ToString());
            }

        }

        // this has to be implemented in the dereived class
        // return the private variable ??
        // Check the new Keyword ... just don't implement it in the base class ??
        public IFeeder Feeder
        {
            get { return feeder; }
        }


        private Exception fatalError(Exception ex)
        {
            try
            {
                SetState(PipelineState.ERROR);
                this.recordFatalError(ex);
                Stage.Release();
                
                // what happens if the Release throws an error ? are we catching it ?
                // sending an event ?
            }
            catch (Exception e)
            {
                this.recordFatalError(e);
            }

            return new Exception("Fatal error halted processing of stage: " + Stage);
        }



    }
}
